package com.tools.common.object;

import javax.annotation.Nonnull;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 自定义类型包装对象
 * 主要针对 Java 各种类型的
 * 操作，比如反射构建对象、强制转换
 * 等等
 * */
@Note("自定义类型包装对象主要针对 Java 各种类型的操作，" +
        "比如反射构建对象、强制转换等等")
public abstract class C<T> implements Comparable<C<T>> {

    @Note("包装的主要 JavaBean 类型的字节码")
    private final Class<?> mainType;

    @Note("如果包装的 JavaBean 对象中含有泛型，那这里就存放泛型（使用递归扫描）")
    private C<?>[] genericTypes;

    @Note("方法形参的类型-参数值包装实体的列表，一般用于获取方法或构造器，并决定要调用时使用")
    private volatile List<TV> argNameAndValues;

    @Note("如果泛型 T 是日期时间类型，那么此处可以指定格式化日期时间的模板字符串")
    private String dateFormatPattern;

    @Note("列举一些常见的类型放入该容器内，减少构建该对象带来的资源消耗")
    private static volatile ConcurrentHashMap<Class<?>, C<?>> CONTAINER;

    @Override
    public int compareTo(@Nonnull C<T> o) {
        return 0;
    }

    /* ***********************************************************************************
     *
     *         构造器和静态构造方法
     *
     * **********************************************************************************
     * */

    protected C() {
        Type superClass = this.getClass().getGenericSuperclass();
        if (superClass instanceof Class) {
            throw new IllegalArgumentException("构造的 C 没有实际类型信息，需要 【new C<具体类型>(){}】 来构建本对象");
        }
        Type topType = ((ParameterizedType) superClass).getActualTypeArguments()[0];
        this.mainType = this.private_init(topType);
    }

    protected C(String dateFormatPattern) {
        this();
        this.dateFormatPattern = dateFormatPattern;
    }

    private C(Class<?> rawType, Type... genericTypes) {
        if(rawType == null) throw new IllegalArgumentException("构造的 C 没有实际原始类型，需要 Class<?> rawType 不为 null");
        if(genericTypes == null) genericTypes = new Class<?>[0];
        ParameterizedTypeImpl type = ParameterizedTypeImpl.make(rawType, genericTypes, null);
        this.mainType = this.private_init(type);
    }

    public static <T> C<T> build(Type type) {
        if(type == null) throw new IllegalArgumentException("构造的 C 没有实际原始类型，需要 type 不为 null");
        if(type instanceof Class) {
            return new C<T>((Class<?>) type) {};
        }
        if(type instanceof TypeVariable) {
            return new C<T>(Object.class) {};
        }
        if(type instanceof WildcardType) {
            WildcardType w = (WildcardType) type;
            if(w.getLowerBounds().length != 0) {
                return build(w.getLowerBounds()[0]);
            }
            if(w.getUpperBounds().length != 0) {
                return build(w.getUpperBounds()[0]);
            }
            return new C<T>(Object.class) {};
        }
        ParameterizedType pt = (ParameterizedType) type;
        return new C<T>((Class<?>) pt.getRawType(), pt.getActualTypeArguments()) {};
    }

    public static <T>C<T> build(Type type, String dateFormatPattern) {
        C<T> result = build(type);
        result.setDateFormatPattern(dateFormatPattern);
        return result;
    }

    /* *******************************************************************************************
     *
     *          普通 Getter / Setter 方法
     *
     * *******************************************************************************************
     * */

    public Class<T> getMainType() {
        return (Class<T>) this.mainType;
    }

    public boolean hasGenericType() {
        return this.genericTypes != null && this.genericTypes.length != 0;
    }

    public int currentGenericCount() {
        return (genericTypes == null) ? 0 : genericTypes.length;
    }

    public C<?> generic(int index) {
        if(genericTypes == null || genericTypes.length == 0) return null;
        if(index < 0 || index >= genericTypes.length) throw new IllegalArgumentException("索引 【" + index + "】 已越界");
        C<?> result = genericTypes[index];
        if(result != null) {
            result.dateFormatPattern = this.dateFormatPattern;
        }
        return result;
    }

    public C<?> genericSafe(int index) {
        if(genericTypes == null || genericTypes.length == 0) return null;
        if(index < 0 || index >= genericTypes.length) return null;
        C<?> result = genericTypes[index];
        if(result != null) {
            result.dateFormatPattern = this.dateFormatPattern;
        }
        return result;
    }

    public String getDateFormatPattern() {
        return dateFormatPattern;
    }

    public void setDateFormatPattern(String dateFormatPattern) {
        this.dateFormatPattern = dateFormatPattern;
    }

    @Override
    public String toString() {
        return "C{" +
                "mainType=" + mainType +
                ", genericTypes=" + Arrays.toString(genericTypes) +
                ", argNameAndValues=" + argNameAndValues +
                ", dateFormatPattern=" + dateFormatPattern +
                '}';
    }

    /* *******************************************************************************************
     *
     *          形参和实参的操作
     *
     * *******************************************************************************************
     * */

    public C<T> add(Class<?> argType) {
        private_lazyLoadTVList();
        this.argNameAndValues.add(new TV(argType));
        return this;
    }

    public C<T> add(Class<?> argType, Object argValue) {
        private_lazyLoadTVList();
        this.argNameAndValues.add(new TV(argType, argValue));
        return this;
    }

    public C<T> del(int argIndex) {
        if(argNameAndValues == null || argNameAndValues.isEmpty()) return this;
        private_checkIndex(argIndex);
        this.argNameAndValues.remove(argIndex);
        return this;
    }


    public C<T> set(int index, Class<?> newArgType) {
        if(argNameAndValues == null || argNameAndValues.isEmpty()) return this;
        private_checkIndex(index);
        TV tv = this.argNameAndValues.get(index);
        tv.setType(newArgType);
        this.argNameAndValues.set(index, tv);
        return this;
    }


    public C<T> set(int index, Object newArgValue) {
        if(argNameAndValues == null || argNameAndValues.isEmpty()) return this;
        private_checkIndex(index);
        TV tv = this.argNameAndValues.get(index);
        tv.setValue(newArgValue);
        this.argNameAndValues.set(index, tv);
        return this;
    }


    public C<T> set(int index, Class<?> newArgType, Object newArgValue) {
        if(argNameAndValues == null || argNameAndValues.isEmpty()) return this;
        private_checkIndex(index);
        TV tv = this.argNameAndValues.get(index);
        tv.setType(newArgType);
        tv.setValue(newArgValue);
        this.argNameAndValues.set(index, tv);
        return this;
    }

    public TV get(int index) {
        if(argNameAndValues == null || argNameAndValues.isEmpty()) return null;
        private_checkIndex(index);
        return this.argNameAndValues.get(index);
    }

    public Class<?> getArgType(int index) {
        if(argNameAndValues == null || argNameAndValues.isEmpty()) return null;
        private_checkIndex(index);
        TV tv = this.argNameAndValues.get(index);
        if(tv == null) return null;
        return tv.type;
    }

    public Object getArgValue(int index) {
        if(argNameAndValues == null || argNameAndValues.isEmpty()) return null;
        private_checkIndex(index);
        TV tv = this.argNameAndValues.get(index);
        if(tv == null) return null;
        return tv.value;
    }


    public Class<?> type(int index) {
        if(argNameAndValues == null || argNameAndValues.isEmpty()) return null;
        private_checkIndex(index);
        return this.argNameAndValues.get(index).getType();
    }


    public Object value(int index) {
        if(argNameAndValues == null || argNameAndValues.isEmpty()) return null;
        private_checkIndex(index);
        return this.argNameAndValues.get(index).getValue();
    }

    public Object orDefaultValue(int index) {
        if(argNameAndValues == null || argNameAndValues.isEmpty()) return null;
        private_checkIndex(index);
        return this.argNameAndValues.get(index).getOrDefaultValue();
    }


    public void copyTo(C<?> c) {
        if(c == null) return;
        c.argNameAndValues = (this.argNameAndValues == null)
                ? null
                : new ArrayList<>(this.argNameAndValues);
        if(this.genericTypes != null) {
            c.genericTypes = (C<?>[]) Array.newInstance(C.class, this.genericTypes.length);
            System.arraycopy(this.genericTypes, 0, c.genericTypes, 0, this.genericTypes.length);
        }
    }

    public boolean hasArgs() {
        return argNameAndValues != null && !argNameAndValues.isEmpty();
    }

    public int argCount() {
        return (argNameAndValues == null) ? 0 : argNameAndValues.size();
    }


    public Class<?>[] argTypeArray() {
        if(argNameAndValues == null || argNameAndValues.isEmpty()) return new Class<?>[0];
        int size = argNameAndValues.size();
        Class<?>[] argValueArray = new Class<?>[size];
        for (int i = 0; i < size; i++) {
            argValueArray[i] = argNameAndValues.get(i).getType();
        }
        return argValueArray;
    }


    public Object[] argValueArray() {
        if(argNameAndValues == null || argNameAndValues.isEmpty()) {
            return new Object[0];
        }
        int size = argNameAndValues.size();
        Object[] argValueArray = new Object[size];
        for (int i = 0; i < size; i++) {
            TV tv = argNameAndValues.get(i);
            argValueArray[i] = tv.hasValue()
                    ? tv.getValue()
                    : tv.getOrDefaultValue();
        }
        return argValueArray;
    }

    /* *******************************************************************************************
     *
     *          静态方法
     *
     * *******************************************************************************************
     * */

    @Note("从容器中获取对应的该类实例，如果不是常见类型则自行创建返回")
    public static C<?> getC(Type mainType) {
        if(mainType instanceof Class) {
            private_lazyLoadContainer();
            C<?> c = CONTAINER.get(mainType);
            if(c == null) return build(mainType);
            return c;
        }
        return build(mainType);
    }

    @Note("从容器中放入对应的 【字节码 - 类实例】，注意二者类型要一致")
    public static <T>void putC(Class<T> mainType, C<T> c) {
        if(mainType == null || c == null) return;
        if(!mainType.equals(c.getMainType())) return;
        private_lazyLoadContainer();
        CONTAINER.put(mainType, c);
    }

    /* *******************************************************************************************
     *
     *          私有逻辑
     *
     * *******************************************************************************************
     * */

    @Note("处理泛型类型组成数组")
    private Class<?> private_handleParent(Class<?> t) {
        Type parentType = t.getGenericSuperclass();
        if(parentType instanceof Class<?>) {
            if(Object.class.equals(parentType)) {
                return t;
            }
            this.private_handleParent((Class<?>) parentType);
            return t;
        }
        if(parentType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) parentType;
            Type[] arguments = pt.getActualTypeArguments();
            if(arguments == null || arguments.length == 0) {
                return t;
            }
            this.genericTypes = new C<?>[arguments.length];
            for (int i = 0; i < arguments.length; i++) {
                this.genericTypes[i] = build(arguments[i]);
            }
        }
        return t;
    }

    @Note("初始化处理泛型的逻辑封装")
    private Class<?> private_init(Type type) {
        if(type instanceof Class<?>) {
            Class<?> t = (Class<?>) type;
            if(t.isArray()) {
                //如果是数组，将数组的 component 类型装在泛型数组中
                this.genericTypes = new C<?>[1];
                this.genericTypes[0] = build(t.getComponentType());
                return t;
            }
            return this.private_handleParent(t);
            //return t;
        }
        ParameterizedType parameterizedType = (ParameterizedType) type;
        Type[] arguments = parameterizedType.getActualTypeArguments();
        if(arguments == null || arguments.length == 0) {
            return (Class<?>) parameterizedType.getRawType();
        }
        this.genericTypes = new C<?>[arguments.length];
        for (int i = 0; i < arguments.length; i++) {
            this.genericTypes[i] = build(arguments[i]);
        }
        return (Class<?>) parameterizedType.getRawType();
    }


    @Note("懒加载 【形参-实参】 包装实体列表，但是非线程安全，BC 对象不建议复用")
    private void private_lazyLoadTVList() {
        if(argNameAndValues == null) {
            this.argNameAndValues = new ArrayList<>(1);
        }
    }

    @Note("验证入参索引，保证不越界")
    private void private_checkIndex(int index) {
        if(index < 0 || index >= argCount()) {
            throw new IllegalArgumentException("索引 【" + index + "】 已越界");
        }
    }


    @Note("懒加载常见类型实例容器类，放入基本类型、包装类、字符串和 Object 类型等等的场景类型")
    private static void private_lazyLoadContainer() {
        if(CONTAINER != null) return;
        synchronized (C.class) {
            if(CONTAINER != null) return;
            CONTAINER = new ConcurrentHashMap<>(25);
            CONTAINER.put(String.class, new C<String>() {});
            CONTAINER.put(Object.class, new C<Object>() {});
            CONTAINER.put(int.class, build(int.class));
            CONTAINER.put(long.class, build(long.class));
            CONTAINER.put(double.class, build(double.class));
            CONTAINER.put(float.class, build(float.class));
            CONTAINER.put(boolean.class, build(boolean.class));
            CONTAINER.put(char.class, build(char.class));
            CONTAINER.put(short.class, build(short.class));
            CONTAINER.put(byte.class, build(byte.class));
            CONTAINER.put(Integer.class, new C<Integer>() {});
            CONTAINER.put(Long.class, new C<Long>() {});
            CONTAINER.put(Double.class, new C<Double>() {});
            CONTAINER.put(Float.class, new C<Float>() {});
            CONTAINER.put(Boolean.class, new C<Boolean>() {});
            CONTAINER.put(Short.class, new C<Short>() {});
            CONTAINER.put(Byte.class, new C<Byte>() {});
            CONTAINER.put(Character.class, new C<Character>() {});
        }
    }

    /* *******************************************************************************************
     *
     *          内部类 - 方法形参和实参的包装实体
     *
     * *******************************************************************************************
     * */

    /**
     * 方法形参和实参的包装实体
     * */
    @Note("方法形参和实参的包装实体")
    public static class TV {

        @Note("形参类型")
        private Class<?> type;

        @Note("实参")
        private Object value;

        @Note("实参是否有值")
        private boolean hasValue;

        public TV(Class<?> type) {
            this.setType(type);
            this.hasValue = false;
        }

        public TV(Class<?> type, Object value) {
            this.setType(type);
            this.value = value;
            this.hasValue = true;
        }

        public Class<?> getType() {
            return type;
        }

        private void setType(Class<?> type) {
            if(type == null) throw new NullPointerException("不允许传入 null 值的类型字节码对象");
            this.type = type;
        }

        public Object getValue() {
            return value;
        }

        public Object getOrDefaultValue() {
            if(hasValue) return value;
            return BasicEnum.getDefaultValue(type);
        }

        private void setValue(Object value) {
            this.value = value;
            if(!hasValue) this.hasValue = true;
        }

        public boolean hasValue() {
            return hasValue;
        }

        @Override
        public String toString() {
            return "{" + type + "=" + value + "("+ hasValue +")}";
        }


        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            TV tv = (TV) o;
            return Objects.equals(type, tv.type) && Objects.equals(value, tv.value) && hasValue == tv.hasValue;
        }

        @Override
        public int hashCode() {
            return Objects.hash(type, value, hasValue);
        }
    }
}
